package com.junchenrobot.mes.core;

import com.github.rholder.retry.*;
import com.junchenrobot.mes.config.PlcProperties;
import com.junchenrobot.mes.exceptions.PLCException;
import com.junchenrobot.mes.handler.PlcClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class AsyncPlcConnector {

    private final PlcProperties properties;

    @Autowired
    public AsyncPlcConnector(PlcProperties properties) {
        this.properties = properties;
    }

    /**
     * 卷绕机准备好
     *
     * @param offset 偏移量
     * @throws ExecutionException 执行异常
     * @throws RetryException     重试异常
     */
    public void winderReady(Integer offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(2L, 120);
        Boolean call = retry.call(() -> plcClient.winderReady(offset));
        if (call) {
            log.info("丝准备好");
        }
    }

    /**
     * 卷绕机推
     *
     * @param offset 偏移量
     * @throws ExecutionException 执行异常
     * @throws RetryException     重试例外
     */
    public void winderPush(Integer offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(2, 30);
        Boolean ready = retry.call(() -> plcClient.winderReady(offset));
        if (ready) {
            log.info("推丝准备好");
        }
        if (ready) {
            Boolean push = retry.call(() -> plcClient.winderStartPush(offset));
            if (push) {
                log.info("推丝完毕");
            }
            if (push) {
                Boolean call = retry.call(() -> plcClient.winderPushFinished(offset));
                if (call == null) {
                    log.info("推丝清除");
                }
            }
        }
    }

    /**
     * 清除信号
     *
     * @param offset 偏移量
     */
    public void winderClear(Integer offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(1L);
        retry.call(() -> plcClient.winderCleanPush(offset));
    }


    /**
     * 转变做好准备
     *
     * @param offset 抵消
     */
    public void transferRequest(int offset,int offset2) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(3L, 1000);
        Boolean call = retry.call(() -> plcClient.transferRequest(offset,offset2));
        if (call) {
            retry.call(() -> plcClient.transferInplace(offset,offset2));
        }
    }

    /**
     * 转变做好准备
     *
     * @param offset 抵消
     */
    public void transferInfo(int offset, int line, int spinning, int type) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(500L, TimeUnit.MILLISECONDS);
        retry.call(() -> plcClient.transferInfo(offset, line, spinning, type));
    }

    public void transferRunning(int offset,int offset2) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(1L);
        retry.call(() -> plcClient.transferRunning(offset,offset2));
    }

    public void transferFinished(int offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(1L);
        retry.call(() -> plcClient.transferFinished(offset));
    }

    public void transferClear(int offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(5L);
        retry.call(() -> plcClient.tranferClearRequest(offset));
    }

    /**
     * 3秒重试一次,重试60次
     * @return
     */
    public Retryer<Boolean> retry() {
        return RetryerBuilder.<Boolean>newBuilder()
                .retryIfResult(result -> Objects.equals(result, Boolean.FALSE))
                .retryIfExceptionOfType(PLCException.class)
                //.withRetryListener(new PlcRetryListener())
                .retryIfRuntimeException()
                .withBlockStrategy(BlockStrategies.threadSleepStrategy())
                .withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))
                .withStopStrategy(StopStrategies.stopAfterAttempt(60))
                .build();
    }
    /**
     * 每几秒重试一次,重试90次
     * @param period 时间数据
     * @return
     */
    public Retryer<Boolean> retry(long period) {
        return RetryerBuilder.<Boolean>newBuilder()
                .retryIfResult(result -> Objects.equals(result, Boolean.FALSE))
                .retryIfExceptionOfType(PLCException.class)
                //.withRetryListener(new PlcRetryListener())
                .retryIfRuntimeException()
                .withBlockStrategy(BlockStrategies.threadSleepStrategy())
                .withWaitStrategy(WaitStrategies.fixedWait(period, TimeUnit.SECONDS))
                .withStopStrategy(StopStrategies.stopAfterAttempt(90))
                .build();
    }
    /**
     * 根据时间数据时间单位重试,重试90次
     * @param period 时间数据
     * @param timeUnit 时间单位
     * @return
     */
    public Retryer<Boolean> retry(long period, TimeUnit timeUnit) {
        return RetryerBuilder.<Boolean>newBuilder()
                .retryIfResult(result -> Objects.equals(result, Boolean.FALSE))
                .retryIfExceptionOfType(PLCException.class)
                //.withRetryListener(new PlcRetryListener())
                .retryIfRuntimeException()
                .withBlockStrategy(BlockStrategies.threadSleepStrategy())
                .withWaitStrategy(WaitStrategies.fixedWait(period, timeUnit))
                .withStopStrategy(StopStrategies.stopAfterAttempt(90))
                .build();
    }
    /**
     * 根据时间单位没几秒重试一次,根据次数数据决定重试多少次
     * @param period 时间数据
     * @param count 次数数据
     * @return
     */
    public Retryer<Boolean> retry(long period, int count) {
        return RetryerBuilder.<Boolean>newBuilder()
                .retryIfResult(result -> Objects.equals(result, Boolean.FALSE))
                .retryIfExceptionOfType(PLCException.class)
                //.withRetryListener(new PlcRetryListener())
                .retryIfRuntimeException()
                .withBlockStrategy(BlockStrategies.threadSleepStrategy())//阻塞策略
                .withWaitStrategy(WaitStrategies.fixedWait(period, TimeUnit.SECONDS))//等待策略
                .withStopStrategy(StopStrategies.stopAfterAttempt(count))//停止策略
                .build();
    }

    /**
     * 开门
     *
     * @param offset 偏移量
     * @throws ExecutionException
     * @throws RetryException
     */
    public void openTheDoor(int offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(1L, 200);
        //写入关门为0
        Boolean call = retry.call(() -> plcClient.closeTheDoor(offset, 0));
        //写入开门保持为1
        Boolean call1 = retry.call(() -> plcClient.doorHolding(offset, 1));
        //读到门开到位为1
        Boolean call2 = retry.call(() -> plcClient.doorInplace(offset));
        if (call2) {
            //写入关门为0
            log.info("关门0");
            retry.call(() -> plcClient.closeTheDoor(offset, 0));
        }

    }

    /**
     * 关门
     *
     * @param offset 偏移量
     * @throws ExecutionException
     * @throws RetryException
     */
    public void closeTheDoor(int offset) throws ExecutionException, RetryException {
        PlcClient plcClient = new PlcClient(properties);
        Retryer<Boolean> retry = retry(1L, 200);
        //写入开门保持为0
        Boolean call = retry.call(() -> plcClient.doorHolding(offset, 0));
        //写入关门为1
        Boolean call1 = retry.call(() -> plcClient.closeTheDoor(offset, 1));
    }
}

